#include "GraphWidget.h"

#include <QPainter>
#include <math.h>


GraphWidget::GraphWidget( QWidget * parent )
    :
	QWidget( parent )
{
    _pi = 4 * atan( 1.0 );
    _cur_value = 0.0;
    _scale_start = 140.0;
    _scale_min = 0.0;
    _scale_max = 100.0;
    _scale_delta   = _scale_max - _scale_min;
    _scale_degrees = 260.0;
}


//! Sets the background color for the graph.
//! \param color The new background color.
void GraphWidget::setBackgroundColor( QColor & color )
{
    _bg_color = color;
}


//! Adds a point to the graph
//! \param value The new point's value
void GraphWidget::addPoint( float value )
{
    _cur_value = value;
    draw();
}

//! Updates the display of the graph.
void GraphWidget::draw()
{
    update();
}

//! Sets the scale for the graph.
//! \param min Minimum value
//! \param max Maximum value
void GraphWidget::setScale( int min, int max )
{
    _scale_min = min;
    _scale_max = max;
}

//////////////////     Signals

//! Slot which takes a new point and adds it to the graph.
//! \param value The new value to be graphed.
void GraphWidget::onNewPoint( float value )
{
    addPoint( value );
}


//////////////////     Event Handlers
void GraphWidget::paintEvent( QPaintEvent * event )
{
    QPainter painter( this );

    /////////////////////////////////////////////////////
    // Determine graph's diameter, radius, and extents //
    /////////////////////////////////////////////////////
    
    int diam    = height() > width() ? width() : height();
    
    int cx      = width()  >> 1;    // width / 2
    int cy      = height() >> 1;    // height / 2

    int radius  = cx > cy ? cy : cx;

    int left	= cx - radius;
    int right	= cy - radius;
    int top	= radius + cx;
    int bottom	= radius + cy;

    // Configure Painter's settings
    painter.setRenderHint( QPainter::Antialiasing );
    painter.setPen( Qt::blue );
    painter.setBackground( Qt::black );

    ////// Draw enclosing rectangle
    painter.drawRect( rect() );
  
    ///////////////////////////////////////////////////
    // Precalculate variables for graph's tick marks //
    ///////////////////////////////////////////////////
    
    double dPiOver180 = _pi / 180.0;
  
    double dRadius = static_cast<double>(radius);
    double rad1 = 0.9 * dRadius;
    double rad2 = 0.7 * dRadius;
  
    double y1, y2, x1, x2;
  
    ///////////////////////////
    // Draw scale tick marks //
    ///////////////////////////
    
    painter.setPen( Qt::red );
  
    for( int i = 0; i < 36; ++i )
    {
        if( i < 5 || i > 13 )
        {
	    double di = static_cast<double>(i);
  
            // Precalculate sin and cos values, as trig
            // operations are very slow
            // TODO: speed up with a LUT (look-up table)
	    double sin5d = sin( di * 10.0 * dPiOver180 );
	    double cos5d = cos( di * 10.0 * dPiOver180 );
  
            // calculate x and y raw coordinates from
            // radius 1 (rad1) and radius 2 (rad2)
	    double r1cos5d = rad1 * cos5d;
	    double r2cos5d = rad2 * cos5d;
	    double r1sin5d = rad1 * sin5d;
	    double r2sin5d = rad2 * sin5d;
  
            // Translate calculated raw points into their final locations
	    x1 = r1cos5d + static_cast<double>(cx);
	    x2 = r2cos5d + static_cast<double>(cx);
	    y1 = r1sin5d + static_cast<double>(cy);
	    y2 = r2sin5d + static_cast<double>(cy);
  
            // Draw the resultant line
	    painter.drawLine( x1, y1, x2, y2 );
        }
    }

    /////////////////////////////
    // Draw the graph's needle //
    /////////////////////////////
    
    drawNeedle( painter, static_cast<double>(radius) * 0.8, static_cast<double>(cx), static_cast<double>(cy) );
}

//! This function draws the analog graph's needle.
//! \param painter This is a reference to the painter with which the needle should be drawn.
//! \param radius This is the radius (length) of the needle.
//! \param cx This is the center, or pivot point, for the needle's x parameter.
//! \param cy This is the center, or pivot point, for the needle's y parameter.
void GraphWidget::drawNeedle( QPainter & painter, double radius, double cx, double cy )
{
    QPen pen( Qt::green, 3 );
    painter.setPen( pen );

    double scale_step = _scale_degrees / _scale_delta;
    double dPiOver180 = _pi / 180.0;

    double needle_pos = scale_step * _cur_value + _scale_start;
    
    if( needle_pos >= 360.0 )
	needle_pos -= 360.0;

    // Calculate needle's enpoint
    double x = radius * cos( needle_pos * dPiOver180 ) + cx;
    double y = radius * sin( needle_pos * dPiOver180 ) + cy;

    painter.drawLine( cx, cy, x, y );
}

//! Calculates the points of a circle.
//! \param cx The \ex coordinate of the center of the circle.
//! \param cy The \ey coordinate of the center of the circle.
//! \param rad The \bradius of the circle
//! \param x The resulting x coordinate.  This parameter is passed by reference, and returned thusly.
//! \param y The resulting y coordinate.  This parameter is passed by reference, and returned thusly.
void GraphWidget::calcCirclePoints( int cx, int cy, int rad, int &x, int &y )
{
    // Use x^2 + y^2 = r^2
    // y = sqrt( r^2 - x^2 )
    // x = sqrt( r^2 - y^2 )
    int xsqrd = cx * cx;
    int ysqrd = cy * cy;
    int rsqrd = rad * rad;

    x = (int) sqrt( (double) rsqrd - ysqrd );
    y = (int) sqrt( (double) rsqrd - xsqrd );
}

//! Handles mouse move events.
//! \param event The data for the mouse move event.
void GraphWidget::mouseMoveEvent( QMouseEvent * event )
{
    QPoint loc = event->pos();
    _cursor = loc;
    _cur_value = ( (float) _cursor.y() / height() ) * _scale_max;

    if( _cur_value > _scale_max )
	_cur_value = _scale_max;
    else if( _cur_value < _scale_min )
	_cur_value = _scale_min;

    update();
}

